"""Git operations manager for fetching and managing PR branches."""

import logging
import os
import shutil
from pathlib import Path
from typing import List, Optional

from git import Repo, GitCommandError
from git.exc import InvalidGitRepositoryError

logger = logging.getLogger(__name__)


class GitManager:
    """Git operations manager."""

    def __init__(self, repo_name: str, ssh_url: str, local_path: str):
        """
        Initialize Git manager.

        Args:
            repo_name: Repository name (e.g., 'org/repo')
            ssh_url: SSH URL for the repository
            local_path: Local path where repository should be cloned
        """
        self.repo_name = repo_name
        self.ssh_url = ssh_url
        self.local_path = Path(local_path).expanduser().resolve()
        self.repo: Optional[Repo] = None

    def ensure_repo_cloned(self) -> bool:
        """
        Ensure repository is cloned locally.

        Returns:
            True if repository is available, False otherwise
        """
        try:
            if self.local_path.exists() and (self.local_path / ".git").exists():
                # Repository already exists, try to open it
                try:
                    self.repo = Repo(self.local_path)
                    logger.info(f"Opened existing repository at {self.local_path}")
                    return True
                except InvalidGitRepositoryError:
                    logger.warning(f"Invalid git repository at {self.local_path}, removing...")
                    shutil.rmtree(self.local_path)

            # Clone repository
            logger.info(f"Cloning repository {self.repo_name} to {self.local_path}")
            self.local_path.parent.mkdir(parents=True, exist_ok=True)
            self.repo = Repo.clone_from(self.ssh_url, self.local_path)
            logger.info(f"Successfully cloned repository to {self.local_path}")
            return True

        except GitCommandError as e:
            logger.error(f"Failed to clone repository {self.repo_name}: {e}")
            return False
        except Exception as e:
            logger.error(f"Unexpected error while cloning repository: {e}")
            return False

    def update_main_branch(self, main_branch: str = "main") -> bool:
        """
        Update main branch from remote.

        Args:
            main_branch: Name of main branch (main or master)

        Returns:
            True if update successful, False otherwise
        """
        if not self.repo:
            if not self.ensure_repo_cloned():
                return False

        try:
            # Fetch latest changes
            logger.info(f"Fetching latest changes for {self.repo_name}")
            self.repo.remotes.origin.fetch()

            # Try to checkout main branch if not already on it
            if self.repo.active_branch.name != main_branch:
                try:
                    self.repo.git.checkout(main_branch)
                except GitCommandError:
                    # Try master if main doesn't exist
                    if main_branch == "main":
                        try:
                            self.repo.git.checkout("master")
                            main_branch = "master"
                        except GitCommandError:
                            logger.error(f"Could not checkout main or master branch")
                            return False

            # Pull latest changes
            self.repo.remotes.origin.pull(main_branch)
            logger.info(f"Updated {main_branch} branch for {self.repo_name}")
            return True

        except GitCommandError as e:
            logger.error(f"Failed to update main branch: {e}")
            return False

    def fetch_pr_branch(self, pr_number: int) -> bool:
        """
        Fetch PR branch from remote.

        Args:
            pr_number: Pull request number

        Returns:
            True if fetch successful, False otherwise
        """
        if not self.repo:
            if not self.ensure_repo_cloned():
                return False

        try:
            # Fetch PR branch
            pr_branch_name = f"pr_{pr_number}"
            refspec = f"+refs/merge-requests/{pr_number}/head:{pr_branch_name}"

            logger.info(f"Fetching PR {pr_number} branch for {self.repo_name}")
            self.repo.remotes.origin.fetch(refspec)

            logger.info(f"Successfully fetched PR {pr_number} branch")
            return True

        except GitCommandError as e:
            logger.error(f"Failed to fetch PR {pr_number} branch: {e}")
            return False

    def checkout_pr_branch(self, pr_number: int, main_branch: str = "main") -> bool:
        """
        Checkout PR branch.

        Args:
            pr_number: Pull request number
            main_branch: Base branch name

        Returns:
            True if checkout successful, False otherwise
        """
        if not self.repo:
            if not self.ensure_repo_cloned():
                return False

        try:
            pr_branch_name = f"pr_{pr_number}"

            # First ensure PR branch is fetched
            if not self.fetch_pr_branch(pr_number):
                return False

            # Checkout PR branch
            logger.info(f"Checking out PR {pr_number} branch")
            self.repo.git.checkout(pr_branch_name)

            logger.info(f"Successfully checked out PR {pr_number} branch")
            return True

        except GitCommandError as e:
            logger.error(f"Failed to checkout PR {pr_number} branch: {e}")
            return False

    def get_diff(self, base_branch: str = "main") -> str:
        """
        Get diff between PR branch and base branch.

        Args:
            base_branch: Base branch to compare against

        Returns:
            Diff string
        """
        if not self.repo:
            logger.error("Repository not initialized")
            return ""

        try:
            # Get diff
            diff = self.repo.git.diff(f"{base_branch}...HEAD")
            logger.info(f"Retrieved diff for {self.repo_name}")
            return diff

        except GitCommandError as e:
            logger.error(f"Failed to get diff: {e}")
            return ""

    def get_changed_files(self, base_branch: str = "main") -> List[str]:
        """
        Get list of changed files in PR.

        Args:
            base_branch: Base branch to compare against

        Returns:
            List of changed file paths
        """
        if not self.repo:
            logger.error("Repository not initialized")
            return []

        try:
            # Get list of changed files
            changed_files = self.repo.git.diff("--name-only", f"{base_branch}...HEAD").split("\n")
            changed_files = [f.strip() for f in changed_files if f.strip()]
            logger.info(f"Found {len(changed_files)} changed files in {self.repo_name}")
            return changed_files

        except GitCommandError as e:
            logger.error(f"Failed to get changed files: {e}")
            return []

    def cleanup_pr_branch(self, pr_number: int) -> bool:
        """
        Clean up PR branch after review.

        Args:
            pr_number: Pull request number

        Returns:
            True if cleanup successful, False otherwise
        """
        if not self.repo:
            return True

        try:
            pr_branch_name = f"pr_{pr_number}"

            # Checkout main branch first
            try:
                self.repo.git.checkout("main")
            except GitCommandError:
                try:
                    self.repo.git.checkout("master")
                except GitCommandError:
                    logger.warning("Could not checkout main/master, skipping branch cleanup")
                    return False

            # Delete local PR branch
            try:
                self.repo.git.branch("-D", pr_branch_name)
                logger.info(f"Cleaned up PR {pr_number} branch")
            except GitCommandError:
                # Branch might not exist, that's okay
                logger.debug(f"PR branch {pr_branch_name} does not exist, skipping deletion")

            return True

        except Exception as e:
            logger.warning(f"Error during PR branch cleanup: {e}")
            return False

